/*
把查询结果映射为对象，假设有一个Student类，成员属性与数据库的学生字段一一对应，怎么把查询结果转换为对象呢
 */
package MyJDBC;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;

public class Result2Object {
    public static void main(String[] args) {
        try(Connection connection = DriverManager.getConnection("jdbc:mysql://192.168.32.130:3306/study", "root", "123456");
            Statement statement = connection.createStatement()){
            ResultSet set = statement.executeQuery("select * from student");
            while(set.next()){
                Student student = new Student(set.getInt(1), set.getString(2),set.getString(3));
                student.say();
            }

            //但上面这种方式存在局限性，针对每个类只能手动去每次转换，不具有普适性
            //我们可以利用反射机制将查询结果映射为对象，使用反射的好处是，无论什么类型都可以通过我们的方法来进行实体映射。convert方法里使用了反射
            ResultSet set2 = statement.executeQuery("select * from teacher");
            while(set2.next()){
                Teacher teacher = convert(set2, Teacher.class);
                if (teacher != null) teacher.say();
            }

        }catch (SQLException e){
            e.printStackTrace();
        }
    }

    private static <T> T convert(ResultSet set, Class<T> clazz){
        try {
            Constructor<T> constructor = clazz.getConstructor(clazz.getConstructors()[0].getParameterTypes()); //获取构造方法对象，默认获取第一个构造方法
            Class<?>[] param = constructor.getParameterTypes();  //获取构造方法的参数列表
            Object[] object = new Object[param.length]; //创建一个object数据，准备存放构造方法参数
            for (int i = 0; i < param.length; i++) {
                object[i] = set.getObject(i+1); //依次把查询结果每个字段的类对象放到object[]数组里，查询结果列数，是从1开始的
                if (object[i].getClass() != param[i])  //判断查询结果的类对象类型和构造方法的参数类型是否相同
                    throw new SQLException("错误的类型转换：" + object[i].getClass() + " -> " + param[i]);
            }
            return constructor.newInstance(object);  //返回构造方法对象，使用newInstance()的方式新创建的对象，把object数组的方法参数传给构造方法对象

        } catch (NoSuchMethodException | SQLException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
    }
}
